// 双向绑定
class Zvue{
    constructor(options){
        this.options = options;
        this.$data = options.data;//获取数据
        this.$el = document.querySelector(options.el);//获取app对象
        // this._directive = {}//这个东西就相当于是deps {dep1:{subs:[watcher]},dep2:{subs:[watcher]}} //这里弃用使用dep 实际上都是一样的 
        // console.log('---options---',options);
        this.Observer(this.$data)
        this.Compile(this.$el)
    }
    // 劫持数据
    Observer(data){
        for (const key in data) {
            const dep = new Dep() //每一个key都有一个dep
            let val = data[key]
            console.log('---observer--',key);
            Object.defineProperty(this.$data,key,{
                get:function(){
                    // 全局属性，相当于指定了一个临时的watcher,用完就丢了
                    if (Dep.target) {
                        dep.addSubs(Dep.target)
                    }
                    return val
                },
                set:function(newValue){
                    if(newValue !== val){
                        val = newValue;
                        dep.notify() //这个通知也是通知的对应的dep中的watcher 
                    }
                }
            })
        }
    }
    // 指令解析
    Compile(el){
        let nodes = el.children;
        for (let i = 0; i < nodes.length; i++) {
            let node = nodes[i]
            // 递归 把嵌套的元素都找出来 判断是否有指令
            if(node.children.length){
                this.Compile(node)
            }
            if(node.hasAttribute('v-text')){
                let attrValue = node.getAttribute('v-text')
                new Watcher(node,this,attrValue,'innerHTML')
                // this._directive[attrValue].push(new Watcher(node,this,attrValue,'innerHTML'))
            }
            if(node.hasAttribute('v-model')){
                let attrValue = node.getAttribute('v-model');
                new Watcher(node,this,attrValue,'value')
                // this._directive[attrValue].push(new Watcher(node,this,attrValue,'value'))
                node.addEventListener('input', () => {
                    this.$data[attrValue] = node.value
                })
            }
        }
    }
}
class Dep{
    target = null;
    constructor(){
        this.subs = [];//可以对应多个watcher
    }
    // 在vue的源码当中，每个组件只有一个watcher,每个watcher也会记录dep 他们是多对多的关系
    addSubs(watcher){
       this.subs.push(watcher)
    }
    notify(){
        this.subs.forEach(watcher =>{
            watcher.update()
        })
    }
}
// 订阅者
class Watcher{
    constructor(el,vm,exp,attr){
        this.el = el;
        this.vm = vm;
        this.exp = exp;
        this.attr = attr;
        this.value = this.get(); // 得到当前vue实例上对应表达式exp的最新的值
        this.update()
    }
    get(){
        Dep.target = this;
        var value = this.vm.$data[this.exp]; //触发get进行依赖收集
        Dep.target = null;
        return value;
    }
    update(){
        this.el[this.attr] = this.vm.$data[this.exp]
    }
}
export default Zvue
// h(tag,props,children)函数就是去返回描述虚拟dom的结构的方法, 标签/属性/子元素
// diff的本质就是让程序知道应该去更新谁




// 1.webpack对文件的变化进行一个监听，当他监听到变化的时候会对对应的模块重新编译打包，并且打包后的代码保存到内存中
// 2.devServe通知浏览器端文件发生了变化，在启动devServer的时候会在服务端和浏览器端建立一个webSocket的长连接，然后devServer监听到webpack新文件打包后，将编译打包后的新模块的hash值发送到浏览器端
//  tips:webpack-dev-server 修改了webpack 配置中的 entry 属性，在里面添加了 webpack-dev-client 的代码，这样在最后的 bundle.js 文件中就会有接收 websocket 消息的代码了。
// 3.浏览器端接收到发来的hash，会根据配置文件进行处理，如果是reload就刷新，如果是热更的话就会把这个hash值交给webpack的热更模块去处理
// 4.  如果webpack热更模块监听到有新的hash,就会向服务端请求，返回更新的文件列表和最新的模块代码
// 5.  热更模块会进行一些处理，找到旧模块，删除缓存过期的模块和依赖，添加新的模块
// 6. 热更失败的话执行reload